#coding=utf8

from grammar_table import GrammarTable, ACT
from ala_classes import AlaGeneralSyntaxError, AlaGrammarTableError, AlaParserError, SyntaxTreeNode, Token


class Parser(object):

    __tokens = None
    __curr = None
    __curr_token = None
    __stack = None
    __grammar = None

    def __init__(self, tokens, table_file_name):
        self.__tokens = tokens
        self.__curr = 0
        self.__curr_token = self.__tokens[self.__curr]
        self.__stack = [0, ]
        self.__grammar = GrammarTable(table_file_name)

    # INTERFACE

    def run(self):
        try:
            while True:
                current_row = self.__stack[-1]
                action = self.__grammar.get_action(current_row, self.__curr_token)
                if action.type == ACT.S:
                    self.__stack_push(self.__curr_token)
                    self.__stack_push(action.value)
                    self.__shift_caret()
                elif action.type == ACT.R:
                    length = self.__grammar.get_production_length(action.value)
                    popped = self.__stack_pop(length * 2)
                    catched = popped[::2]
                    line = None
                    for part in reversed(catched):
                        line = part.line
                    another_row = self.__stack[-1]
                    parent = SyntaxTreeNode(self.__grammar.get_production_value(action.value), catched, line)
                    self.__stack_push(parent)
                    for part in catched:
                        part.parent = parent
                    next_row = self.__grammar.get_goto_value(another_row, action.value)
                    self.__stack_push(next_row)
                elif action.type == ACT.ACC:
                    popped = self.__stack_pop(1 * 2)
                    catched = popped[::2]
                    return catched[0]
                else:
                    raise AlaParserError("Unknown action ID")
        except AlaGeneralSyntaxError as e:
            raise AlaGrammarTableError(u"Синтаксическая ошибка", line_number=e.line_number)
        except Exception:
            raise AlaGeneralSyntaxError(u"Неизвестная синтаксическая ошибка")

    # PRIVATE METHODS

    def __shift_caret(self):
        self.__curr += 1
        self.__curr_token = self.__tokens[self.__curr]

    def __stack_push(self, elem):
        self.__stack.append(elem)

    def __stack_pop(self, count):
        return [self.__stack.pop() for _ in xrange(count)][::-1]